Mapping I

GESIS Workshop: Introduction to Geospatial Techniques for Social Scientists in R

Stefan Jünger & Dennis Abel

2025-04-09

Now

Day Time Title
April 09 10:00-11:30 Introduction
April 09 11:30-11:45 Coffee Break
April 09 11:45-13:00 Data Formats
April 09 13:00-14:00 Lunch Break
April 09 14:00-15:30 Mapping I
April 09 15:30-15:45 Coffee Break
April 09 15:45-17:00 Spatial Wrangling
April 10 09:00-10:30 Mapping II
April 10 10:30-10:45 Coffee Break
April 10 10:45-12:00 Applied Spatial Linking
April 10 12:00-13:00 Lunch Break
April 10 13:00-14:30 Spatial Autocorrelation
April 10 14:30-14:45 Coffee Break
April 10 14:45-16:00 Spatial Econometrics & Outlook

Fun with flags… MAPS!

Fun with Flags by Dr. Sheldon Cooper. Big Bang Theory

Fun with maps

plot() does not allow us to manipulate the maps in an easy. But we already have the two most essential ingredients to create a nice map:

  1. Vector data stored in the ./data folder
  2. Some (hopefully) interesting attributes linked with the geometries.

Cologne data

attributes_cologne <- sf::read_sf("./data/attributes_cologne.shp")  

charger_cologne <- sf::read_sf("./data/charger_cologne.shp")

streets_cologne <- sf::read_sf("./data/streets_cologne.shp")

inhabitants_cologne <- terra::rast("./data/inhabitants_cologne.tif")

What makes a good map?

Good Mapping

  • reduction to most important information
  • legends, scales, descriptions
  • audience oriented
  • adjusted for color vision deficiencies

Bad Mapping

  • Overcrowding and overlapping
  • Unreadable information
  • Missing information like the legend or source
  • Poor choice of color palettes

What makes a good map?

… but there is one other type:

The fast but nice map.

  • fast exploration of spatial data by visualizing the geometries and attributes
  • might not be publication-ready yet, but they are more rewarding than just plotting information.

The choice is yours: R packages for mapping

As always, R offers several ways to map spatial data, and the provided packages are various. What is out there? Just a few:

  • Base R graphics package: mapdata
  • Mobile-friendly interactive maps: leaflet
  • Interactive and static thematic maps based on shapefiles:
  • tmap
  • mapview

Our choice for today

Today, we’ll concentrate on the package tmap

  • Very intuitive and makes ‘good’ decisions for us
  • The syntax is very similar to ggplot21

Source

First map: Cologne electric car data

library(tmap)

tm_shape(attributes_cologne) +
  tm_fill("ecar")

tmap in a nutshell

There is not much to consider when using tmap but two essential requirements:

  1. Define your spatial object.
  2. Choose a building block to determine how to display information.
# define and introduce every (new) 
# geospatial data object
tm_shape() +
  
  # choose at least one building block as 
  # 'aesthetic layer'
  
  # for polygon layer choose from:
  tm_fill() + # polygons without borders
  tm_polygons() +  # polygons with borders
  tm_borders() + # only borders of polygons
  
  # for line layer choose:
  tm_lines() +
  
  # for point layer choose:
  tm_dots() +
  tm_bubbles() +
  
  # for raster layer choose
  tm_raster() +
  tm_rgb() +
  
  ...

# for all of them:
?'tmap-element'

tmap in a nutshell: polygon layer

tm_shape(attributes_cologne) +
  tm_fill()
tm_shape(attributes_cologne) +
  tm_polygons()
tm_shape(attributes_cologne) +
  tm_borders()

tmap in a nutshell: line and point layer

tm_shape(streets_cologne) +
  tm_lines()
tm_shape(charger_cologne) +
  tm_dots()

tmap in a nutshell: raster layer

tm_shape(inhabitants_cologne) +
  tm_raster()

tmap in a nutshell: put it all together

We can map the geometric attributes as single layers, but we can also layer our map and stack the layers on each other.

tm_shape(attributes_cologne) +
  tm_polygons(fill_alpha = .8) +
  tm_shape(streets_cologne) +
  tm_lines(col = "red") +
  tm_shape(inhabitants_cologne) +
  tm_raster(col_alpha = .8) +
  tm_shape(charger_cologne) +
  tm_dots()

Add some information

After we took care of our geometric types, we want to add some information to our data. The inner construction of each building block of tm_elements is the same.

  1. Define the variable of interest first by stating the column name.
  2. Add a name for the legend title and color palette, adjust the legend and scales …

Choosing an attribute

tm_shape(attributes_cologne) +
  tm_polygons("ecar") 

Choosing a color palette

tm_shape(attributes_cologne) +
  tm_polygons(
    "ecar",
    fill.scale = 
      tm_scale_continuous(values = "brewer.rd_pu"),
    fill.legend = 
      tm_legend(title = "Electric Cars")
  ) 

Finding out which color suits you

Finding the correct color palette is a bit tricky. Run the following command to open a nice graphical interface that helps you with that:

cols4all::c4a_gui()

Re-placing the legend

tm_shape(attributes_cologne) +
  tm_polygons(
    "ecar",
    fill.scale = 
      tm_scale_continuous(values = "brewer.rd_pu"),
    fill.legend = 
      tm_legend(title = "Electric Cars")
  ) +
  tm_layout(legend.outside = TRUE)

Compasses & scale bars

tm_shape(attributes_cologne) +
    tm_polygons(
    "ecar",
    fill.scale = 
      tm_scale_continuous(values = "brewer.rd_pu"),
    fill.legend = 
      tm_legend(title = "Electric Cars")
  ) +
  tm_scalebar(position = c("left", "bottom")) +
  tm_compass(position = c("right", "top"))

Exercise 3_1: Basic Maps

Exercise

What’s left?

Getting interactive!

ecar_map <-
tm_shape(attributes_cologne) +
  tm_borders(col = "black") +
  tm_polygons("ecar")

tmap_mode("view")

ecar_map

Facets I

tm_shape(attributes_cologne) +
  tm_polygons(c("cdu", "spd", "greens", "afd", "left", "fdp")) +
  tm_facets_stack(ncol = 3)

Facets II

attributes_cologne |> 
  dplyr::mutate(city_area = as.integer(substr(id, 1, 1))) |> 
  tm_shape() +
  tm_polygons(c("ecar")) +
  tm_facets_wrap(by = "city_area", nrow = 2) +
  tm_layout(panel.labels = c("1 Innenstadt","2 Rodenkirchen","3 Lindenthal",
                             "4 Ehrenfeld", "5 Nippes","6 Chorweiler",
                             "7 Porz", "8 Kalk", "9 Mülheim")) 

Change the overall style

tm_shape(attributes_cologne) +
  tm_polygons("ecar") +
  # tmap_style() shows all available styles
  tm_style("classic") 

Color vision deficiencies: standard palette

Created with the package colorBlindness

Color vision deficiencies: viridis palette

Created with the package colorBlindness

Animation

tm_animate() allows you to create animated maps and visualize changes in spatial data, e.g. over time or attributes. You can customize the animation’s duration, frame rate, timeline sliders, and transition effects.

vote_gif <- 
  attributes_cologne |> 
  tidyr::pivot_longer(
    # Select all columns to pivot
    cols = 
      c("cdu", "spd", "greens", "afd", 
        "left", "fdp"),  
    # New column name for party identifier
    names_to = "party_id",  
    # New column name for vote shares
    values_to = "voteshare" 
  ) |> 
  tm_shape() +
  tm_polygons(c("voteshare")) +
  tm_facets_grid(pages = "party_id")  

tmap_animation(
  vote_gif, 
  filename = "../img/vote_gif.gif",
  delay=150
) 

Save Maps

tmap offers support for various output formats, including:

  • Static Images: PNG, JPEG, TIFF, etc.
  • Interactive Web Maps: HTML, JavaScript (using packages like leaflet).
  • Animated Maps: GIF, MP4, etc.

You can also control width and height, dpi, etc.

# save regular map
tmap_save(ecar_map, filename = "ecar_map.png")

# save as interactive map
tmap_save(ecar_map, filename = "ecar_map.html" )

Note On Mapping Responsible

In the best cases, maps are easy to understand and an excellent way to transport (scientific) messages.

In the worst cases, they simplify (spurious) correlations and draw a dramatic picture of the world.

Maps can shape narratives

  • Decisions on which projection you use (remember the true size projector?),
  • the segment of the world you choose,
  • and the colors and styles you add have a strong influence.

Example: Kenneth Field’s blog post

Exercise 3_2: Fun with Maps

Exercise